﻿using NotificationLib;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace GalacticLib
{
    public class DataSetLoader
    {

        SqlConnection Connection;
        System.Timers.Timer WriteToDbTimer;
        /// <summary>
        /// Delay between checks for new records in the queue
        /// </summary>
        short WriteToDbDelayMS = 1;

        int DataSetLoadRequestId;
        int DataSetId;
        int UserId;
        bool SendEmail;
        bool SendPushover;
        string FileName;
        string RegexString;
        string CommentMarkers;
        string FieldMap;
        string UserEmail;
        string PushoverKey;
        string Username;

        Regex regexObject;
        Queue<string> Lines;
        Queue<DataSetData> DataItems = new Queue<DataSetData>();
        Dictionary<string, int> FieldMapDictionary = new Dictionary<string,int>();

        bool AllLinesInQueue = false;
        bool AllItemsInDatabase = false;

        long RecordsToProcess = 0;
        long RecordsProcessed = 0;
        
        public DataSetLoader(string connectionString)
        {
            Connection = new SqlConnection(connectionString);

        }

        SqlCommand GetSprocCommand(SqlConnection connection, string sprocName)
        {
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = Connection;
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            cmd.CommandText = sprocName;

            if (cmd.Connection.State != System.Data.ConnectionState.Open)
                cmd.Connection.Open();
            
            return cmd;
        }

        public void Run()
        {
            if (CheckForRequest())
            {
                // parse the field=1,field=2 information into a dictionary
                
                foreach(string s in FieldMap.Split(','))
                {
                    string[] mapItem = s.Split('=');
                    FieldMapDictionary.Add(mapItem[0], int.Parse(mapItem[1]));
                }

                regexObject = new Regex(RegexString, RegexOptions.Compiled);

               /* Console.WriteLine("Request: " + DataSetLoadRequestId + Environment.NewLine +
                                  "File: " + FileName
                        );
                */
                WriteToDbTimer = new System.Timers.Timer(WriteToDbDelayMS);
                WriteToDbTimer.Elapsed += WriteToDbTimer_Elapsed;
                WriteToDbTimer.AutoReset = false;
                WriteToDbTimer.Start();




                using (StreamReader r = new StreamReader(FileName))
                {
                    List<string> commentCharList = new List<string>();
                    commentCharList =  CommentMarkers.Split(',').ToList();

                    string l;
                    while ((l = r.ReadLine()) != null)
                    {
                        if(l == string.Empty)
                            continue;

                        if (!commentCharList.Contains(l[0].ToString()))
                        {
                            RecordsToProcess++;
                        }
                    }
                   // Console.WriteLine("Total lines: " + RecordsToProcess);


                    SqlCommand cmd = GetSprocCommand(Connection, "usp_StartDataLoadProgress");

                    cmd.Parameters.Add(new SqlParameter("id", DataSetLoadRequestId));
                    cmd.Parameters.Add(new SqlParameter("TotalRows", RecordsToProcess));
                   
                    cmd.ExecuteNonQuery();

                    r.BaseStream.Seek(0, SeekOrigin.Begin);

                    

                   

                    while ((l = r.ReadLine()) != null)
                    {
                        if(l == string.Empty)
                            continue;
                        //pushing lines to line queue

                        //make sure line isn't a comment
                        if (! commentCharList.Contains(l[0].ToString()))
                        {
                            //push into lines queue
                            DataSetData dsd;

                            if (DataSetData.TryParse(l, regexObject, FieldMapDictionary, out dsd))
                            {                             
                                lock (DataItems)
                                {
                                    DataItems.Enqueue(dsd);
                                }
                            }
                        }
                    }

                    AllLinesInQueue = true;

                    while (!AllItemsInDatabase)
                    {
                       // Console.WriteLine("All items in database is false");
                        Console.WriteLine("Remaining Items: " + DataItems.Count);
                        Thread.Sleep(1000); // 
                    }
                    

                }
            }


            if (SendEmail || SendPushover)
            {
                Config cfg = new Config(Connection);

                SqlCommand cmd = new SqlCommand();
                cmd.Connection = Connection;
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                cmd.CommandText = "usp_GetUserNotificationSettings";

                cmd.Parameters.Add(new SqlParameter("UserId", UserId));

              

                SqlDataReader reader = cmd.ExecuteReader();
                reader.Read();
                string userEmail = reader["Email"].ToString();
                string userPushoverKey = reader["PushoverKey"].ToString();
                

                if (SendEmail)
                {
                   // Console.WriteLine("Sending email notification to " + userEmail);
                    EmailNotification notif = new EmailNotification(cfg.EmailSettings, "Dataset Load Complete", "Dataset Load Complete", userEmail);
                    notif.Send();
                }

                if (SendPushover)
                {
                  //  Console.WriteLine("Sending pushover notification to " + userPushoverKey);
                    PushoverNotification pushOver = new PushoverNotification(cfg.PushoverSettings, userPushoverKey, "Datset Load Complete", "Dataset Load Complete");
                    pushOver.Send();
                }
            }


            Cleanup();
        }

        void WriteToDbTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("WriteToDbTimer_Elapsed");
            bool go;
            lock(DataItems)
            {
                go = DataItems.Count > 0;
            }

            while (go)
            {
                DataSetData dsd;
                lock (DataItems)
                {
                    dsd = DataItems.Dequeue();
                    go = DataItems.Count > 0;
                }                
                //Console.WriteLine("Sending to DB: " + dsd.ToString());
                WriteToDatabase(dsd);
                RecordsProcessed++;
                //Thread.Sleep(5);
            }

            lock (DataItems)
            {
                Console.WriteLine("End of while loop in WriteToDbTimer_Elapsed");
                if (DataItems.Count == 0 && AllLinesInQueue)
                {
                    AllItemsInDatabase = true;
                    WriteToDbTimer.Stop();

                    SqlCommand cmd = GetSprocCommand(Connection, "usp_DataSetLoadComplete");

                    cmd.Parameters.Add(new SqlParameter("DataSetLoadRequestId", DataSetLoadRequestId));

                    cmd.ExecuteNonQuery();

                }
                else
                    WriteToDbTimer.Start();
            }
           
        }

        void WriteToDatabase(DataSetData dsd)
        {
            // negative z values are not useful
            if (dsd.z < 0)
                return;


            SqlCommand cmd = GetSprocCommand(Connection, "usp_AddData");

            cmd.Parameters.Add(new SqlParameter("DataSetLoadRequestId", DataSetLoadRequestId));
            cmd.Parameters.Add(new SqlParameter("DataSetId", DataSetId));
            cmd.Parameters.Add(new SqlParameter("GalaxyName", dsd.GalaxyName));
            cmd.Parameters.Add(new SqlParameter("Morphology", dsd.Morphology));
            cmd.Parameters.Add(new SqlParameter("glon", dsd.glon));
            cmd.Parameters.Add(new SqlParameter("glat", dsd.glat));
            cmd.Parameters.Add(new SqlParameter("z", dsd.z));
            cmd.Parameters.Add(new SqlParameter("v", dsd.v));
            cmd.Parameters.Add(new SqlParameter("Distance", dsd.Distance));

            cmd.ExecuteNonQuery();
        }

        bool CheckForRequest()
        {

            SqlCommand cmd = GetSprocCommand(Connection, "usp_GetDataSetLoadRequest");
            SqlDataReader reader = cmd.ExecuteReader();


            if (reader.Read())
            {
                DataSetLoadRequestId = int.Parse( reader["id"].ToString());
                DataSetId = int.Parse(reader["DataSetId"].ToString());
                UserId = int.Parse(reader["UserId"].ToString());
                SendEmail = bool.Parse(reader["Email"].ToString());
                SendPushover = bool.Parse(reader["Pushover"].ToString());
                FileName = (string)reader["FileName"];
                RegexString = (string)reader["Regex"];
                CommentMarkers = (string)reader["CommentMarkers"];
                FieldMap = (string)reader["FieldMap"];
                UserEmail = (string)reader["UserEmail"];
                PushoverKey = (string)reader["PushoverKey"];
                Username = (string)reader["Username"];

                reader.Close();
                return true;

            }

            reader.Close();
            return false;

        }

        void Cleanup()
        {
            Connection.Close();
        }



    }
}
